package cc.ttiot.service.dataCollection.io.impl;

import cc.ttiot.service.dataCollection.io.IoCollection;
import com.pi4j.io.gpio.*;

import java.util.Arrays;

import static cc.ttiot.utils.StringUtils.byte2short;
import static cc.ttiot.utils.TimeUtils.busyWaitMicros;

/**
 * @Description: TODO
 * @Author WangSuheng
 * @Date 2021/10/6 20:00
 * @Version 1.0
 */

//参考: https://www.codeleading.com/article/53374853048/

//参考: https://www.jianshu.com/p/8077e28db96a
public class TemperatureAndHumidityCollection implements IoCollection {
    private static volatile TemperatureAndHumidityCollection temperatureAndHumidityCollection;

    //将DHT22引脚设置为数字输入引脚，启用其内部上拉电阻器
    private GpioPinDigitalMultipurpose pin;

    {
        try {
            init();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    private TemperatureAndHumidityCollection() {
        super();
    }

    //双检索单例防止多次初始化
    public static TemperatureAndHumidityCollection getInstance() {
        if (temperatureAndHumidityCollection == null) {
            synchronized (TemperatureAndHumidityCollection.class) {
                if (temperatureAndHumidityCollection == null) {
                    temperatureAndHumidityCollection = new TemperatureAndHumidityCollection();
                }
            }
        }
        return temperatureAndHumidityCollection;
    }

    //初始化串口
    public void init() throws InterruptedException {
        pin = GPIO.provisionDigitalMultipurposePin(GpioFactory.getDefaultProvider(), DHT22_PIN,
                PinMode.DIGITAL_OUTPUT, PinPullResistance.PULL_UP);
        pin.setState(PinState.HIGH);
        Thread.sleep(5000);
    }

    // 读取数据
    // java的byte位运算也是先转换为int再位运算，右移时要注意符号位的问题
    // java由于时间片的粒度问题无法设置微妙级别时延(Thread.sleep(milli,nano)也没用);
    // 调用本地方法jni微秒的控制也很不准确，并且出现了在连读40次左右之后读出数据全是1的情况
    // jni方法 : delayMicroseconds(4);
    private byte[] readData() throws InterruptedException {
        int count = 0;
        byte[] data = new byte[5];

        if (pin.getMode() != PinMode.DIGITAL_OUTPUT) {
            pin.setMode(PinMode.DIGITAL_OUTPUT);
        }

        pin.setState(PinState.LOW);
        busyWaitMicros(1000);
        pin.setState(PinState.HIGH);

        busyWaitMicros(30);

        pin.setMode(PinMode.DIGITAL_INPUT);

        count = 100;
        while (pin.getState() == PinState.HIGH) {
            if ((--count) > 0) {
                busyWaitMicros(1);
            } else {
                return null;
            }
        }

        count = 100;
        while (pin.getState() == PinState.LOW) {
            if ((--count) > 0) {
                busyWaitMicros(1);
            } else {
                return null;
            }
        }

        count = 100;
        while (pin.getState() == PinState.HIGH) {
            if ((--count) > 0) {
                busyWaitMicros(1);
            } else {
                return null;
            }
        }

        //一共五个字节
        for (int i = 0; i < 5; i++) {
            //每个字节填满
            for (int j = 0; j < 8; j++) {
                count = 100;
                while (pin.getState() == PinState.LOW) {
                    if ((--count) > 0) {
                        busyWaitMicros(1);
                    } else {
                        return null;
                    }
                }

                busyWaitMicros(30);

                data[i] <<= 1;
                if (pin.getState() == PinState.HIGH) {
                    data[i] |= 1;

                    if (i * j != 28) {
                        count = 100;
                        while (pin.getState() == PinState.HIGH) {
                            if ((--count) > 0) {
                                busyWaitMicros(1);
                            } else {
                                return null;
                            }
                        }
                    }

                }
            }

        }

        pin.setMode(PinMode.DIGITAL_OUTPUT);
        pin.setState(PinState.HIGH);
        return data;
    }

    //判断数据是否正确
    private boolean validate(byte[] data) {
        return (byte) (data[0] + data[1] + data[2] + data[3]) == data[4];
    }

    //单次采集
    public short[] collect() throws InterruptedException {
        byte[] data = readData();

        //读到错数据的情况
        if (data == null) return null;

//        for (byte datum : data) {
//            String t = Integer.toBinaryString(datum);
//            if (t.length() > 8) {
//                System.out.println(t.substring(t.length() - 8));
//            } else {
//                System.out.println(t);
//            }
//        }

        //读到数据不合法的情况
        if (!validate(data)) {
            return null;
        }
        short[] res = new short[2];
        //湿度(真实数据10倍)
        res[0] = byte2short(Arrays.copyOfRange(data, 0, 2));
        //温度(真实数据10倍)
        res[1] = byte2short(Arrays.copyOfRange(data, 2, 4));
        return res;
    }

    @Override
    public PinState getState() {
        return pin.getState();
    }

}
